package com.zlict.commonelasticsearch.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

import javax.net.ssl.SSLContext;


/**
 * Elasticsearch配置类 - 适配 ES 8.13.4
 * 
 * @author zlict
 */
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig {

    private String host = "localhost";
    private int port = 9200;
    private String scheme = "http";
    
    // 认证配置
    private String username;
    private String password;
    
    // 连接配置
    private Connection connection = new Connection();
    
    // 连接池配置
    private Pool pool = new Pool();
    
    // SSL 配置
    private Ssl ssl = new Ssl();
    
    // Getter 和 Setter
    public String getHost() {
        return host;
    }
    
    public void setHost(String host) {
        this.host = host;
    }
    
    public int getPort() {
        return port;
    }
    
    public void setPort(int port) {
        this.port = port;
    }
    
    public String getScheme() {
        return scheme;
    }
    
    public void setScheme(String scheme) {
        this.scheme = scheme;
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getPassword() {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public Connection getConnection() {
        return connection;
    }
    
    public void setConnection(Connection connection) {
        this.connection = connection;
    }
    
    public Pool getPool() {
        return pool;
    }
    
    public void setPool(Pool pool) {
        this.pool = pool;
    }
    
    public Ssl getSsl() {
        return ssl;
    }
    
    public void setSsl(Ssl ssl) {
        this.ssl = ssl;
    }
    
    /**
     * 连接配置内部类
     */
    public static class Connection {
        private String timeout = "10s";
        private String socketTimeout = "60s";
        private String requestTimeout = "5s";
        
        public String getTimeout() {
            return timeout;
        }
        
        public void setTimeout(String timeout) {
            this.timeout = timeout;
        }
        
        public String getSocketTimeout() {
            return socketTimeout;
        }
        
        public void setSocketTimeout(String socketTimeout) {
            this.socketTimeout = socketTimeout;
        }
        
        public String getRequestTimeout() {
            return requestTimeout;
        }
        
        public void setRequestTimeout(String requestTimeout) {
            this.requestTimeout = requestTimeout;
        }
        
        /**
         * 解析时间配置为毫秒
         */
        public int getTimeoutMs() {
            return parseTimeToMs(timeout);
        }
        
        public int getSocketTimeoutMs() {
            return parseTimeToMs(socketTimeout);
        }
        
        public int getRequestTimeoutMs() {
            return parseTimeToMs(requestTimeout);
        }
        
        private int parseTimeToMs(String time) {
            if (time.endsWith("s")) {
                return Integer.parseInt(time.substring(0, time.length() - 1)) * 1000;
            } else if (time.endsWith("ms")) {
                return Integer.parseInt(time.substring(0, time.length() - 2));
            }
            return Integer.parseInt(time);
        }
    }
    
    /**
     * 连接池配置内部类
     */
    public static class Pool {
        private int maxConnections = 100;
        private int maxConnectionsPerRoute = 50;
        
        public int getMaxConnections() {
            return maxConnections;
        }
        
        public void setMaxConnections(int maxConnections) {
            this.maxConnections = maxConnections;
        }
        
        public int getMaxConnectionsPerRoute() {
            return maxConnectionsPerRoute;
        }
        
        public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
            this.maxConnectionsPerRoute = maxConnectionsPerRoute;
        }
    }
    
    /**
     * SSL 配置内部类
     */
    public static class Ssl {
        private boolean verifyCertificates = true;
        private boolean verifyHostname = true;
        private boolean trustAllCertificates = false;
        
        public boolean isVerifyCertificates() {
            return verifyCertificates;
        }
        
        public void setVerifyCertificates(boolean verifyCertificates) {
            this.verifyCertificates = verifyCertificates;
        }
        
        public boolean isVerifyHostname() {
            return verifyHostname;
        }
        
        public void setVerifyHostname(boolean verifyHostname) {
            this.verifyHostname = verifyHostname;
        }
        
        public boolean isTrustAllCertificates() {
            return trustAllCertificates;
        }
        
        public void setTrustAllCertificates(boolean trustAllCertificates) {
            this.trustAllCertificates = trustAllCertificates;
        }
    }
    
    /**
     * 创建低级RestClient实例 - 适配 ES 8.13.4
     * 支持 HTTPS 和 SSL 证书验证
     * 
     * @return RestClient实例
     */
    @Bean
    public RestClient restClient() {
        // 构建HTTP主机
        HttpHost httpHost = new HttpHost(
            this.getHost(), 
            this.getPort(), 
            this.getScheme()
        );
        
        // 创建RestClient构建器
        RestClientBuilder builder = RestClient.builder(httpHost);
        
        // 配置请求参数
        builder.setRequestConfigCallback(requestConfigBuilder ->
            requestConfigBuilder
                .setConnectTimeout(this.getConnection().getTimeoutMs())
                .setSocketTimeout(this.getConnection().getSocketTimeoutMs())
                .setConnectionRequestTimeout(this.getConnection().getRequestTimeoutMs())
        );
        
        // 配置HTTP客户端
        builder.setHttpClientConfigCallback(httpClientBuilder -> {
            // 设置连接池参数
            httpClientBuilder
                .setMaxConnTotal(this.getPool().getMaxConnections())
                .setMaxConnPerRoute(this.getPool().getMaxConnectionsPerRoute());
            
            // 配置认证
            if (this.getUsername() != null && this.getPassword() != null) {
                configureAuthentication(httpClientBuilder);
            }
            
            // 配置 SSL
            if ("https".equals(this.getScheme())) {
                configureSSL(httpClientBuilder);
            }
            
            return httpClientBuilder;
        });
        
        return builder.build();
    }
    
    /**
     * 创建现代 Elasticsearch Java API 客户端 - 适配 ES 8.13.4
     * 
     * @return ElasticsearchClient实例
     */
    @Bean
    public ElasticsearchClient elasticsearchClient(RestClient restClient) {
        // 创建传输层
        ElasticsearchTransport transport = new RestClientTransport(
            restClient, 
            new JacksonJsonpMapper()
        );
        
        // 创建现代 Elasticsearch 客户端
        return new ElasticsearchClient(transport);
    }
    
    /**
     * 配置认证设置
     * 
     * @param httpClientBuilder HTTP客户端构建器
     */
    private void configureAuthentication(org.apache.http.impl.nio.client.HttpAsyncClientBuilder httpClientBuilder) {
        // 创建认证提供者
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
            AuthScope.ANY,
            new UsernamePasswordCredentials(this.getUsername(), this.getPassword())
        );
        
        // 设置认证提供者
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
    }
    
    /**
     * 配置 SSL 设置
     * 
     * @param httpClientBuilder HTTP客户端构建器
     */
    private void configureSSL(org.apache.http.impl.nio.client.HttpAsyncClientBuilder httpClientBuilder) {
        try {
            if (!this.getSsl().isVerifyCertificates() || this.getSsl().isTrustAllCertificates()) {
                // 跳过证书验证
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new javax.net.ssl.TrustManager[]{
                    new javax.net.ssl.X509TrustManager() {
                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                        
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                        }
                        
                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                        }
                    }
                }, new java.security.SecureRandom());
                
                httpClientBuilder.setSSLContext(sslContext);
                
                // 设置主机名验证器
                if (!this.getSsl().isVerifyHostname()) {
                    httpClientBuilder.setSSLHostnameVerifier((hostname, session) -> true);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to configure SSL", e);
        }
    }
}

